package com.xxl.sso.core.filter;

import com.xxl.sso.core.conf.Conf;
import com.xxl.sso.core.constant.UrlConstant;
import com.xxl.sso.core.login.SsoTokenLoginHelper;
import com.xxl.sso.core.login.SsoWebLoginHelper;
import com.xxl.sso.core.path.impl.AntPathMatcher;
import com.xxl.sso.core.user.XxlSsoUser;
import com.xxl.sso.core.util.CookieUtil;
import com.xxl.sso.core.util.HttpUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * web sso filter
 *
 * @author xuxueli 2018-04-03
 */
public class XxlSsoWebFilter extends HttpServlet implements Filter {
    private static Logger logger = LoggerFactory.getLogger(XxlSsoWebFilter.class);

    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private String ssoServer;
    private String logoutPath;
    private String excludedPaths;
    private String excludedUnLoginPaths;


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        ssoServer = filterConfig.getInitParameter(Conf.SSO_SERVER);
        logoutPath = filterConfig.getInitParameter(Conf.SSO_LOGOUT_PATH);
        excludedPaths = filterConfig.getInitParameter(Conf.SSO_EXCLUDED_PATHS);
        excludedUnLoginPaths = filterConfig.getInitParameter(Conf.SSO_EXCLUDED_UNLOGIN_PATHS);

        logger.info("XxlSsoWebFilter init.");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        // make url
        String servletPath = req.getServletPath();


        // excluded path check
        if (excludedPaths!=null && excludedPaths.trim().length()>0) {


            for (String excludedPath:excludedPaths.split(",")) {
                String uriPattern = excludedPath.trim();
                // 支持ANT表达式
                if (antPathMatcher.match(uriPattern, servletPath)) {
                    chain.doFilter(request, response);
                    return;
                }

            }

        }

        // logout path check
        if (logoutPath!=null
                && logoutPath.trim().length()>0
                && logoutPath.equals(servletPath)) {

            // remove cookie
            SsoWebLoginHelper.removeSessionIdByCookie(req, res);

            // redirect logout
            String logoutPageUrl = ssoServer.concat(Conf.SSO_LOGOUT);
            res.sendRedirect(logoutPageUrl);

            return;
        }

        // valid login user, cookie + redirect
        XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(req, res);

        // valid login fail
        if (xxlUser == null) {


            if(Conf.ALREADY_GET_USERINFO.equals(request.getParameter(Conf.IS_ALREADY_GET_USERINFO))){

                chain.doFilter(request, response);
                return;
            }
            //未登录时这些链接无需转到sso-server的预登录地址/login
            if (excludedUnLoginPaths!=null && excludedUnLoginPaths.trim().length()>0) {
                for (String excludedPath:excludedUnLoginPaths.split(",")) {
                    String uriPattern = excludedPath.trim();
                    // 支持ANT表达式
                    if (antPathMatcher.match(uriPattern, servletPath)) {
//                        // total link
                        String requestURL = getRequestURL(req);


                        // redirect login
                        String userinfoUrl = ssoServer.concat(Conf.SSO_USER_INFO)
                                + "?" + Conf.REDIRECT_URL + "=" + requestURL ;

                        //添上查询参数
                        String queryString = req.getQueryString();
                        if(!StringUtils.isEmpty(queryString)){
                            userinfoUrl = userinfoUrl+"?"+queryString;
                        }

                        res.sendRedirect(userinfoUrl);
                        return ;

////                        尝试获取xxlUser，没有就正常访问，有就将xxlUser保存到request中
//                        xxlUser = loginCheckWithRemoteGetSessionId(req,res);
//                        if(xxlUser!=null){
//                            request.setAttribute(Conf.SSO_USER, xxlUser);
//                        }
//                        chain.doFilter(request, response);
//                        return;
                    }
                }
            }



            String header = req.getHeader("content-type");
            boolean isJson=  header!=null && header.contains("json");
            if (isJson) {//application/json类型的请求头

                // json msg
                res.setContentType("application/json;charset=utf-8");
                res.getWriter().println("{\"code\":"+Conf.SSO_LOGIN_FAIL_RESULT.getCode()+", \"msg\":\""+ Conf.SSO_LOGIN_FAIL_RESULT.getMsg() +"\"}");
                return;
            } else {

                // total link
                String link = getRequestURL(req);

                String queryString = req.getQueryString();

                // redirect login
                String loginPageUrl = ssoServer.concat(Conf.SSO_LOGIN)
                        + "?" + Conf.REDIRECT_URL + "=" + link + "&"+queryString;

                res.sendRedirect(loginPageUrl);
                return;
            }

        }

        // ser sso user
        request.setAttribute(Conf.SSO_USER, xxlUser);


        // already login, allow
        chain.doFilter(request, response);
        return;
    }

    /**
    * @Description: 发送http请求获取sessionId,然后通过sessionId获取xxlUser
    * @Param: [request, response]
    * @return: com.xxl.sso.core.user.XxlSsoUser
    * @Author: fourforfo
    * @Date: 2022/8/26
    */
    private XxlSsoUser loginCheckWithRemoteGetSessionId(HttpServletRequest request,HttpServletResponse response) {
        try {
            String host = request.getScheme()+"://"+ UrlConstant.AUTH_FOURMALL_URL;

            Map<String,String> headers = new HashMap<>();
            headers.put("user-agent",request.getHeader("user-agent"));
            headers.put("accept",request.getHeader("accept"));


            //发送http请求获取sessionId
            HttpResponse httpResponse = HttpUtils.doGet(host, Conf.SSO_GET_USER_INFO, "get", headers,null);
            if(httpResponse!=null && httpResponse.getStatusLine().getStatusCode()==200){
                if(httpResponse.getEntity()!=null){
                    String sessionId = httpResponse.getEntity().toString();
                    if(!StringUtils.isEmpty(sessionId)) {
                        //通过sessionId获取xxlUser
                        XxlSsoUser xxlUser = SsoTokenLoginHelper.loginCheck(sessionId);
                        if (xxlUser != null) {
                            CookieUtil.set(response, Conf.SSO_SESSIONID, sessionId, false);    // expire when browser close （client cookie）
                            return xxlUser;
                        }
                    }
                }
            }
        }catch(Exception e){
            new Exception("从sso-server获取sessionId失败！请看下面打印的错误日志：").printStackTrace();
            e.printStackTrace();
        }
        return null;
    }


    public static String getRequestURL(HttpServletRequest request) {
        String hostUrl = request.getHeader("x-forwarded-host");

        if(StringUtils.isEmpty(hostUrl)){

            return request.getRequestURL().toString();
        }else{
            //如果是经由gateway网关的域名请求
            StringBuffer url = new StringBuffer();
            String scheme = request.getScheme();

            url.append(scheme);
            url.append("://");
            url.append(hostUrl);
            url.append(request.getRequestURI());
            return url.toString();
        }

    }

}
